package t150

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"
)

func evalRPN(tokens []string) int {

	stack := &MyStack{}

	for _, token := range tokens {
		switch token {
		case "+":
			fallthrough
		case "-":
			fallthrough
		case "*":
			fallthrough
		case "/":
			el := cal(stack, token)
			stack.Push(strconv.Itoa(el))
			//fmt.Println(stack)
		default:
			stack.Push(token)
		}
	}

	n, err := strconv.Atoi(fmt.Sprintf("%s", stack))
	if err != nil {
		return 0
	}

	return n
}

func cal(stack *MyStack, token string) int {
	el1, _ := stack.Pop()
	el2, _ := stack.Pop()
	n1, _ := strconv.Atoi(el1)
	n2, _ := strconv.Atoi(el2)
	switch token {
	case "+":
		return n2 + n1
	case "-":
		return n2 - n1
	case "*":
		return n2 * n1
	case "/":
		return n2 / n1
	}

	return 0
}

type MyStack struct {
	items []string
	l     sync.Mutex
}

func (s *MyStack) Push(x string) {
	s.l.Lock()
	defer s.l.Unlock()
	s.items = append(s.items, x)
}

func (s *MyStack) Pop() (string, error) {
	s.l.Lock()
	defer s.l.Unlock()
	if len(s.items) == 0 {
		return "", errors.New(": empty stack")
	}
	el := s.items[len(s.items)-1]
	s.items = s.items[:len(s.items)-1]
	return el, nil
}

func (s *MyStack) Top() (string, error) {
	s.l.Lock()
	defer s.l.Unlock()
	if len(s.items) == 0 {
		return "", errors.New(": empty stack")
	}
	el := s.items[len(s.items)-1]
	return el, nil
}

func (s MyStack) String() string {
	return strings.Join(s.items, " ")
}
